"""
!/usr/bin/env python
-*- coding: utf-8 -*-
@Author  : MarkJia
@Site    : 
@File    : 32、装饰器.py
"""


# 函数引用
def study():
    print('好好学习')


MackJia = study()  # 函数的直接调用

# 把函数对象赋值给一个变量
MackPeng = study
print(MackPeng)  # <function study at 0x0000026E6002F160>
# 调用函数
MackPeng()

"""
闭包函数：
    闭包的内部函数中，对外部作用域的变量进行引用
    闭包无法修改外部函数的局部变量
    闭包可以保存当前的运行环境
"""


# 需求：打印每个学生的姓名，年龄，年级
def student_fun(name, age, grage):
    print(f'学生名称是{name},年龄是{age},年级是{grage}年级')


# 结果：学生名称是MackJia,年龄是18,年级是1年级
student_fun('MackJia', 18, 1)
student_fun('XiaoMing', 13, 1)
student_fun('XiaoHong', 12, 1)


# 闭包函数
# 1、闭包的内部函数，对外部作用域的变量进行进行引用
# 2、闭包函数不能改变外部函数的局部变量

def output_fun(grage):
    def inner_fun(name, age):
        print(f'学生名称是{name},年龄是{age},年级是{grage}年级')

    return inner_fun


# 把外部函数output_fun赋值给一个变量
student1 = output_fun(1)
# 结果：是内部函数对象<function output_fun.<locals>.inner_fun at 0x0000017FE025AAE8>
print(student1)
student1('MackJia', 18)
student1('XiaoMing', 13)
student1('XiaoHong', 12)

# python装饰器
"""
行业需求： 涉及 python 技术栈，面试常见题
使用需求： 优化代码的可读性，可维护性
"""


# 案例：函数在开始执行和结束执行时分别打印一个提示信息
def study():
    print('好好学习')


print('函数执行开始')
study()
print('函数执行结束')


# ============ 优化 ==============
def function_tips():
    print('函数开始执行')
    study()
    print('函数执行结束')


function_tips()


def study():
    print('好好学习')


def study2():
    print('好好学习测开')


# ============== 再次优化 ================

# 第二步优化，把中间的执行函数，使用参数代替
def function_tips(func):
    print('函数开始执行')
    # 不再是写死的任何一个函数，而是任意一个外部传入的函数对象
    func()
    print('函数执行结束')


# 传入一个函数对象
function_tips(study)
function_tips(study2)


# ============= 装饰器 ================
# 装饰器
# 自定义一个装饰器
# 第一步：定义两个函数：一个外函数，一个内函数
# 第五步：在装饰器执行过程中，自动传入一个需要调用该装饰器的函数对象：func参数
def timer(func):
    # 第二步：在函数中：添加装饰器的执行逻辑
    def inner():
        print('计时开始')
        func()  # 第六步： 添加需要调用该装饰器函数的执行
        print('计时结束')

    # 第三步：定义外函数的返回值：把内函数的对象 return 出去   记住：一定是没有()的内部函数对象
    return inner


# 第四步：装饰器的使用
@timer
def study():
    print('好好学习')


"""
计时开始
好好学习
计时结束
"""

# 调用函数
study()

# ============= 练习 =============
# 练习：实现一个计时器的装饰器，计算函数的执行时间
import datetime
import time


def time_run(func):
    def inner():
        a = datetime.datetime.now()
        print(f'函数执行开始时间为{a}')
        func()
        print(f'函数执行所用时间为{datetime.datetime.now() - a}')

    return inner


@time_run
def sleep_five():
    time.sleep(5)


sleep_five()


# ================= 被装饰函数带参数 ===========================
# 被装饰函数带参数
def time_run(func):
    # 如果被装饰的函数有参数，需要在内函数加形参。在函数参数调用时添加参数信息
    # 如果不知道被装饰函数的参数数量时，用不定长参数
    def inner(*args, **kwargs):
        a = time.time()
        print(f'函数执行开始时间为{a}')
        func(*args, **kwargs)
        print(f'函数执行所用时间为{time.time() - a}')

    return inner


@time_run
def sojo(name):
    time.sleep(2)
    print(f'{name}客户验收')


# 双杰1#厂房EAP客户验收
a = sojo('双杰1#厂房EAP')
